﻿namespace WebPacker.Tests.Packs.Watchers
{
	using System;
	using System.Threading;
	using NUnit.Framework;
	using WebPacker.Packs.Resources;
	using WebPacker.Packs.Watchers;

	[TestFixture]
	public class FileWatcherTest : ResourceWatcherBaseTest
	{
		private const string Category = "fileMonitor";

		private readonly string testFilePath1 = TestHelper.GetSamplePath("test1.js", Category);
		private readonly string testFilePath2 = TestHelper.GetSamplePath("test2.js", Category);
		private readonly string testFilePath3 = TestHelper.GetSamplePath("jquery-1.6.2.js", Category);

		[SetUp]
		public void PrepareSampleFiles()
		{
			TestHelper.PrepareSampleFiles(new string[] { "test1.js", "test2.js", @"source\jquery-1.6.2.js" }, Category);
		}

		[Test]
		public void DelayChangeTest()
		{
			var signalCount = 0;

			using (var monitor = GetMonitor(500))
			{
				var resource = new FileResource(testFilePath1);
				Exception testException = null;

				monitor.Add(resource);

				monitor.Changed += (sender, ea) =>
				{
					try
					{
						Assert.AreEqual(monitor, sender);
						Assert.AreEqual(resource.Name, ea.ResourceName);
						signalCount++;
					}
					catch (Exception e)
					{
						testException = e;
					}
				};

				for (var i = 0; i < 3; i++)
				{
					ModifyFile(testFilePath1);
					Thread.Sleep(50);
				}

				Thread.Sleep(800);

				if (testException != null)
				{
					throw testException;
				}

				Assert.AreEqual(1, signalCount);
			}
		}

		[Test]
		public void ChangedTest()
		{
			var signal = false;

			using (var monitor = GetMonitor(10))
			{
				var resource = new FileResource(testFilePath1);
				Exception testException = null;

				monitor.Add(resource);

				monitor.Changed += (sender, ea) =>
				{
					try
					{
						Assert.AreEqual(monitor, sender);
						Assert.AreEqual(resource.Name, ea.ResourceName);
						signal = true;
					}
					catch (Exception e)
					{
						testException = e;
					}
				};

				ModifyFile(testFilePath1);
				Thread.Sleep(100);

				if (testException != null)
				{
					throw testException;
				}

				Assert.IsTrue(signal);
			}
		}

		[Test]
		public void ChangedComplexTest()
		{
			using (var monitor = GetMonitor(10))
			{
				var resources = new IResource[] { new FileResource(testFilePath1), new FileResource(testFilePath2), new FileResource(testFilePath3) };
				var signals = new bool[resources.Length];
				Exception testException = null;

				monitor.Set(resources);

				monitor.Changed += (sender, ea) =>
				{
					try
					{
						Assert.IsNotNull(ea);
						Assert.AreEqual(monitor, sender);

						for (var i = 0; i < resources.Length; i++)
						{
							if (ea.ResourceName == resources[i].Name)
							{
								if (signals[i])
								{
									Assert.Fail("Change event for {0} is caused twice.", ea.ResourceName);
								}

								signals[i] = true;
								return;
							}
						}

						Assert.Fail("Unknown change event for {0}.", ea.ResourceName);
					}
					catch (Exception e)
					{
						testException = e;
					}
				};

				foreach (var item in resources)
				{
					ModifyFile(item.Name);
				}

				Thread.Sleep(600);

				if (testException != null)
				{
					throw testException;
				}

				foreach (var item in signals)
				{
					if (!item)
					{
						Assert.Fail("Change event is not caused for one of resources.");
					}
				}
			}
		}

		protected override IResourceWatcher GetMonitor()
		{
			return GetMonitor(100);
		}

		protected override IResource GetNotAcceptableResource()
		{
			return new RemoteResource("http://code.jquery.com/jquery-1.6.2.js");
		}

		protected override IResource GetTestResource()
		{
			return new FileResource(testFilePath1);
		}

		protected override IResource[] GetTestResources()
		{
			return new IResource[] 
			{ 
				new FileResource(testFilePath1), 
				new FileResource(testFilePath2), 
				new FileResource(testFilePath3) 
			};
		}

		private string GetSampleFile(string sample)
		{
			return TestHelper.GetSamplePath(sample, "fileMonitor");
		}

		private IResourceWatcher GetMonitor(int notifyDelay)
		{
			return new FileWatcher
			{
				NotifyDelay = notifyDelay
			};
		}
	}
}
